2022
Jan
01

這篇文章說明如果 Mock private Method ,幫助你減化 unit test 範圍,進而提升寫測試的意願與讓測試碼更好維護。

假如你想測試 class 中的 Method A ,而這個 Method A 分別又去 call Method B / C ,為了降低測試的範圍,我們會希望 Mock Method B 和 C 的 return 值 (mock 是指模擬物件的行為)。

B / C 如果是 public 或是 protected method ,我們只要使用 mockito or spock 就能輕鬆做 到 mock,困難的是如果想 mock 的對象是 private method , private method 只有 object 自已可以 access ,其它 test framework 都沒辦法對 private method 下手,目前唯有 PowerMock ,它直接修改了 java class byte-code ,把 private 改成 public accessible,讓測試碼能直接操作 private method 。

Example
  1. Method A
  2. - Call private Method B
  3. - Call private Method C

powerMock 使用範例

  • 我有一個 class App.java,其中 log 是 public , logInternal 是我想 mock 的 private method
App.java
  1. package com;
  2.  
  3. public class App {
  4.  
  5. public App() {
  6.  
  7. }
  8.  
  9. public String log() {
  10. System.out.println("run log");
  11. return this.logInternal();
  12. }
  13.  
  14. private String logInternal() {
  15. System.out.println("run logInternal");
  16. return "internal";
  17. }
  18.  
  19. }
  • class AppTest.java: 需要使用 PowerMock 中的 createPartialMock / expectPrivate / replay 這 3 個
AppTest.java
  1. package com;
  2.  
  3. import org.junit.Test;
  4. import org.junit.runner.RunWith;
  5. import static org.junit.Assert.assertEquals;
  6.  
  7. import org.powermock.api.easymock.PowerMock;
  8. import org.powermock.core.classloader.annotations.PrepareForTest;
  9. import org.powermock.modules.junit4.PowerMockRunner;
  10.  
  11. @RunWith(PowerMockRunner.class)
  12. @PrepareForTest(App.class)
  13. public class AppTest {
  14.  
  15. @Test
  16. public void TestLog() {
  17. try {
  18. App app = PowerMock.createPartialMock(App.class, "logInternal");
  19. PowerMock.expectPrivate(app, "logInternal").andReturn("mock_result");
  20. PowerMock.replay(app);
  21. String ret = app.log();
  22. assertEquals("Result is " + ret, ret, "mock_result");
  23. } catch (Exception e) {
  24. e.printStackTrace();
  25. }
  26.  
  27.  
  28. }
  29. }
  • pom.xml dependency
Example
  1. <dependency>
  2. <groupId>org.powermock</groupId>
  3. <artifactId>powermock-module-junit4</artifactId>
  4. <version>2.0.9</version>
  5. <scope>test</scope>
  6. </dependency>
  7. <dependency>
  8. <groupId>org.powermock</groupId>
  9. <artifactId>powermock-api-mockito2</artifactId>
  10. <version>2.0.9</version>
  11. <scope>test</scope>
  12. </dependency>
  13. <dependency>
  14. <groupId>org.powermock</groupId>
  15. <artifactId>powermock-api-easymock</artifactId>
  16. <version>2.0.9</version>
  17. <scope>test</scope>
  18. </dependency>

build.gradle

使用 gradle 的話,build config 可以參考這份

Example
  1. plugins {
  2. id "application"
  3. id "java"
  4. }
  5.  
  6. application {
  7. mainClassName = 'com.App'
  8. }
  9.  
  10.  
  11. allprojects {
  12. repositories {
  13. mavenCentral()
  14. }
  15. }
  16.  
  17. dependencies {
  18. def powerMockVersion="2.0.9"
  19. implementation (
  20. "org.powermock:powermock-module-junit4:${powerMockVersion}",
  21. "org.powermock:powermock-api-mockito2:${powerMockVersion}",
  22. "org.powermock:powermock-api-easymock:${powerMockVersion}"
  23. )
  24. }

參考資料


回應 (Leave a comment)